/*
 * server_socket.cpp
 *
 *  Created on: 2021年1月27日
 *      Author: kimfeng
 */

#include "server_socket.h"

#include <boost/shared_ptr.hpp>
#include <boost/bind/bind.hpp>

server_socket::server_socket(const std::string &address,
		const std::string &port, std::size_t io_service_pool_size, bool reuse_port) :
		m_reuse_port(reuse_port),
		m_address(address), m_port(port),
		m_io_svc_pool(io_service_pool_size),
	    m_signals(m_io_svc_pool.get_io_service()),
	    m_acceptor(m_io_svc_pool.get_io_service()),
		m_manager(m_io_svc_pool.get_io_service())
{
	m_signals.add(SIGINT);
	m_signals.add(SIGTERM);
#if defined(SIGQUIT)
  m_signals.add(SIGQUIT);
#endif // defined(SIGQUIT)
	m_signals.async_wait(boost::bind(&server_socket::handle_stop, this));
}

void server_socket::run()
{
#if BOOST_ASIO_VERSION <= 101010
	boost::asio::ip::tcp::resolver resolver(m_acceptor.get_io_service());
	boost::asio::ip::tcp::resolver::query query(m_address, m_port);
	boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
#else
	boost::asio::ip::tcp::resolver resolver(m_acceptor.get_executor());
	boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(m_address,
			m_port).begin();
#endif
	m_acceptor.open(endpoint.protocol());
	if (m_reuse_port)
		m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
	m_acceptor.bind(endpoint);
	m_acceptor.listen();
	log_info("server %s:%u starting...", endpoint.address().to_string().c_str(),
			endpoint.port());

	start_accept();

	m_io_svc_pool.run();
}

void server_socket::start_accept()
{
	connection_ptr c(new connection(m_io_svc_pool.get_io_service(), m_manager,
					m_request_handler));
	m_acceptor.async_accept(c->socket(),
			boost::bind(&server_socket::handle_accept, this, c,
					boost::asio::placeholders::error));
}

void server_socket::handle_accept(connection_ptr c, const boost::system::error_code &e)
{
	if (!e)
	{
		//Accept successed and add connection to manager
		log_info("connected by remote address %s:%u",
				c->socket().remote_endpoint().address().to_string().c_str(),
				c->socket().remote_endpoint().port());
		m_manager.start(c);
	}
	else
	{
		log_error("%s", e.message().c_str());
	}
	start_accept();
}

void server_socket::handle_stop()
{
	m_io_svc_pool.stop();
}

boost::asio::io_service& server_socket::get_io_service()
{
	return m_io_svc_pool.get_io_service();
}

connection_ptr server_socket::get_connection(connection_id_t id)
{
	return m_manager.get_connection(id);
}
